import uiautomation
import ctypes
import re
import win32gui
import win32con
import time

# 全面放弃 Selenium ,转用 Uiautomation.
# 1.目前需要解决的痛点就是关于某些控件不可见.但是可以搜索到的元素.
# 2.这部分的控件是不可见的,但却是已经加载进 Document 了.
# 3.还有一个功能就是如何使网页缩小的功能.

# 内部函数

def Send_Click(hwnd, x, y, button='left', click_type='single'):
    """向指定窗口的指定坐标发送鼠标点击消息。

    Args:
        hwnd (int): 目标窗口的句柄。
        x (int): 点击位置的 x 坐标。
        y (int): 点击位置的 y 坐标。
        button (str): 点击的鼠标按键,可选值为 'left' 或 'right',默认为 'left'。
        click_type (str): 点击类型,可选值为 'single' 或 'double',默认为 'single'。

    Returns:
        bool: 若点击模拟成功返回 True,否则返回 False。
    """
    try:
        if hwnd == 0:
            print("无效的窗口句柄。")
            return False

        # 将坐标转换为 Windows 消息所需的格式
        lParam = y << 16 | x

        if button == 'left':
            if click_type == 'single':
                # 发送鼠标左键按下消息
                win32gui.SendMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam)
                # 发送鼠标左键释放消息
                win32gui.SendMessage(hwnd, win32con.WM_LBUTTONUP, 0, lParam)
            elif click_type == 'double':
                # 发送鼠标左键按下消息
                win32gui.SendMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam)
                # 发送鼠标左键释放消息
                win32gui.SendMessage(hwnd, win32con.WM_LBUTTONUP, 0, lParam)
                # 发送鼠标左键双击消息
                win32gui.SendMessage(hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, lParam)
        elif button == 'right':
            if click_type == 'single':
                # 发送鼠标右键按下消息
                win32gui.SendMessage(hwnd, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, lParam)
                # 发送鼠标右键释放消息
                win32gui.SendMessage(hwnd, win32con.WM_RBUTTONUP, 0, lParam)
            elif click_type == 'double':
                # 发送鼠标右键按下消息
                win32gui.SendMessage(hwnd, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, lParam)
                # 发送鼠标右键释放消息
                win32gui.SendMessage(hwnd, win32con.WM_RBUTTONUP, 0, lParam)
                # 发送鼠标右键双击消息
                win32gui.SendMessage(hwnd, win32con.WM_RBUTTONDBLCLK, win32con.MK_RBUTTON, lParam)

        return True
    except Exception as e:
        print(f"发送点击消息时出错: {e}")
        return False

def Post_Click(hwnd, x, y, button='left', click_type='single'):
    """向指定窗口的指定坐标发送异步鼠标点击消息。

    Args:
        hwnd (int): 目标窗口的句柄。
        x (int): 点击位置的 x 坐标。
        y (int): 点击位置的 y 坐标。
        button (str): 点击的鼠标按键,可选值为 'left' 或 'right',默认为 'left'。
        click_type (str): 点击类型,可选值为 'single' 或 'double',默认为 'single'。

    Returns:
        bool: 若消息发送成功返回 True,否则返回 False。
    """
    try:
        if hwnd == 0:
            print("无效的窗口句柄。")
            return False

        # 将坐标转换为 Windows 消息所需的格式
        lParam = y << 16 | x

        if button == 'left':
            if click_type == 'single':
                # 发送鼠标左键按下消息
                win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam)
                # 发送鼠标左键释放消息
                win32gui.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, lParam)
            elif click_type == 'double':
                # 发送鼠标左键按下消息
                win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam)
                # 发送鼠标左键释放消息
                win32gui.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, lParam)
                # 发送鼠标左键双击消息
                win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, lParam)
        elif button == 'right':
            if click_type == 'single':
                # 发送鼠标右键按下消息
                win32gui.PostMessage(hwnd, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, lParam)
                # 发送鼠标右键释放消息
                win32gui.PostMessage(hwnd, win32con.WM_RBUTTONUP, 0, lParam)
            elif click_type == 'double':
                # 发送鼠标右键按下消息
                win32gui.PostMessage(hwnd, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, lParam)
                # 发送鼠标右键释放消息
                win32gui.PostMessage(hwnd, win32con.WM_RBUTTONUP, 0, lParam)
                # 发送鼠标右键双击消息
                win32gui.PostMessage(hwnd, win32con.WM_RBUTTONDBLCLK, win32con.MK_RBUTTON, lParam)

        return True
    except Exception as e:
        print(f"发送点击消息时出错: {e}")
        return False

def GetControl_Center(Control: uiautomation.Control):
    """获取控件的中心坐标。

    Args:
        Control (uiautomation.Control): 控件对象。

    Returns:
        tuple: 控件中心的 x 和 y 坐标。
    """
    _center_x = int(Control.BoundingRectangle.left + Control.BoundingRectangle.width() / 2)
    _center_y = int(Control.BoundingRectangle.top + Control.BoundingRectangle.height() / 2)    
    return _center_x,_center_y

class Custom_WebUI():
    """自定义的 WebUI 类，用于操作网页元素。"""

    def __init__(self) -> None:
        """初始化 Custom_WebUI 类实例。"""
        self.root = uiautomation.GetRootControl()
        self._window = self.root.WindowControl(ClassName='Chrome_WidgetWin_1',searchDepth=3)
        self.get_doc()
        self._window_hwnd = uiautomation.WindowControl(RegexName='.*?Microsoft.*?Edge').NativeWindowHandle
        self._docu_hwnd = self._docu.GetParentControl().NativeWindowHandle
        self._current_element

    @property
    def current_element(self):
        """获取当前元素。

        Returns:
            uiautomation.Control: 当前元素。
        """
        return self._current_element

    @current_element.setter
    def current_element(self, value):
        """设置当前元素。

        Args:
            value (uiautomation.Control): 要设置的当前元素。
        """
        self._current_element = value

    def get_doc(self):
        """获取文档元素。

        Returns:
            uiautomation.Control: 文档元素。
        """
        self._docu = self._window.PaneControl(Name='Chrome Legacy Window',ClassName='Chrome_RenderWidgetHostHWND').GetChildren()[0]
        self._current_element = self._docu
        return self._docu
    
    def update_doc(self):
        """更新文档元素。

        Returns:
            Custom_WebUI: 当前实例。
        """
        self.get_doc()
        return self
    
    def find(self, Name: str, from_current=False):
        """查找指定名称的元素。

        Args:
            Name (str): 元素的名称。
            from_current (bool): 是否从当前元素开始查找,默认为 False。

        Returns:
            Custom_WebUI: 当前实例。
        """
        if from_current:
            self._current_element = self._current_element.Control(Name=Name)
            return self
        self._current_element = self._docu.Control(Name=Name)
        return self
    
    def Control(self,*args,**kwargs):
        self._current_element = self._docu.Control(*args,**kwargs)
        return self
    
    def focus(self):
        """设置当前元素焦点。

        Returns:
            Custom_WebUI: 当前实例。
        """
        self._current_element.SetFocus()
        return self
    
    def hover(self):
        """将鼠标悬停在当前元素中心。

        Returns:
            Custom_WebUI: 当前实例。
        """
        self._current_element.MoveCursorToMyCenter()
        return self
    
    def left(self):
        """对当前元素执行左键点击。

        Returns:
            Custom_WebUI: 当前实例。
        """
        self._current_element.Click()
        return self
    
    def right(self):
        """对当前元素执行右键点击。

        Returns:
            Custom_WebUI: 当前实例。
        """
        self._current_element.RightClick()
        return self
    
    def type(self, str):
        """向当前元素发送按键。

        Args:
            str (str): 要发送的字符串。

        Returns:
            Custom_WebUI: 当前实例。
        """
        self._current_element.SendKeys(str)
        return self

    def zoom_out(self):
        """缩小网页。

        Returns:
            Custom_WebUI: 当前实例。
        """
        while True:
            应用栏按钮 = self._window.ToolBarControl(Name='应用栏').ButtonControl(Name='设置及其他 (Alt+F)')
            应用栏按钮.SetFocus()
            应用栏按钮.Click()
            当前缩放菜单栏 =self._window.MenuItemControl(RegexName="缩放(Z)*")
            当前缩放菜单栏.SetFocus()
            当前缩放菜单栏.MoveCursorToMyCenter()
            当前值 = int(re.search(r"\d+",当前缩放菜单栏.Name).group())
            if 当前值 >= 100:
                self._docu.SetFocus()
                self._docu.Click()
                break
            else:
                [当前缩放菜单栏.GetChildren()[1].Click() for _ in range(7)]
                self._docu.SetFocus()
                self._docu.Click()
                break
        return self
    
    def zoom_in(self):
        """放大网页。

        Returns:
            Custom_WebUI: 当前实例。
        """
        while True:
            应用栏按钮 = self._window.ToolBarControl(Name='应用栏').ButtonControl(Name='设置及其他 (Alt+F)')
            应用栏按钮.SetFocus()
            应用栏按钮.Click()
            当前缩放菜单栏 =self._window.MenuItemControl(RegexName="缩放(Z)*")
            当前缩放菜单栏.SetFocus()
            当前缩放菜单栏.MoveCursorToMyCenter()
            当前值 = int(re.search(r"\d+",当前缩放菜单栏.Name).group())
            if 当前值 <= 25:
                self._docu.SetFocus()
                self._docu.Click()
                break
            else:
                [当前缩放菜单栏.GetChildren()[0].Click() for _ in range(5)]
                self._docu.SetFocus()
                self._docu.Click()
        return self
    
    def wheeldown(self, times=None):
        """向下滚动当前元素。

        Args:
            times (int, optional): 滚动次数,默认为 None 表示单次滚动。

        Returns:
            Custom_WebUI: 当前实例。
        """
        if times == None:
            self._current_element.WheelDown()
        else:
            self._current_element.WheelDown(wheelTimes=times)
        return self
    
    def wheelup(self, times=None):
        """向上滚动当前元素。

        Args:
            times (int, optional): 滚动次数,默认为 None 表示单次滚动。

        Returns:
            Custom_WebUI: 当前实例。
        """
        if times == None:
            self._current_element.WheelUp()
        else:
            self._current_element.WheelUp(wheelTimes=times)
        return self
        
    def wait(self, secounds):
        """等待指定秒数。

        Args:
            secounds (int): 等待的秒数。

        Returns:
            Custom_WebUI: 当前实例。
        """
        uiautomation.time.sleep(secounds)
        return self

    def lock(self):
        """锁定输入。

        Returns:
            Custom_WebUI: 当前实例。
        """
        ctypes.windll.user32.BlockInput(True)
        return self
    def unlock(self):
        """解锁输入。

        Returns:
            Custom_WebUI: 当前实例。
        """
        ctypes.windll.user32.BlockInput(False)
        return self

    def s_left(self):
        """模拟在当前元素上执行左键点击。

        Returns:
            Custom_WebUI: 当前实例。
        """
        _center_x = self._current_element.BoundingRectangle.left + self._current_element.BoundingRectangle.width() / 2
        _center_y = self._current_element.BoundingRectangle.top + self._current_element.BoundingRectangle.height() / 2
        left = win32gui.GetWindowRect(self._window_hwnd)[0]
        top = win32gui.GetWindowRect(self._window_hwnd)[1]
        Send_Click(hwnd=self._window_hwnd,x=int(_center_x-left),y=int(_center_y-top))
        return self
    
    def s_right(self):
        """模拟在当前元素上执行右键点击。

        Returns:
            Custom_WebUI: 当前实例。
        """
        _center_x = self._current_element.BoundingRectangle.left + self._current_element.BoundingRectangle.width() / 2
        _center_y = self._current_element.BoundingRectangle.top + self._current_element.BoundingRectangle.height() / 2
        left = win32gui.GetWindowRect(self._window_hwnd)[0]
        top = win32gui.GetWindowRect(self._window_hwnd)[1]
        Send_Click(hwnd=self._window_hwnd,x=int(_center_x-left),y=int(_center_y-top),button="right")
        return self

    def s_wheelup(self, times=None):
        """模拟滚动条向上滚动。

        Args:
            times (int, optional): 滚动次数,默认为 None 表示单次滚动。

        Returns:
            Custom_WebUI: 当前实例。
        """
        if times is None:
            times = 1
        for _ in range(times):
            win32gui.SendMessage(self._window_hwnd, win32con.WM_VSCROLL, win32con.SB_LINEUP, 0)
        return self

    def s_wheeldown(self, times=None):
        """模拟滚动条向下滚动。

        Args:
            times (int, optional): 滚动次数,默认为 None 表示单次滚动。

        Returns:
            Custom_WebUI: 当前实例。
        """
        if times is None:
            times = 1
        for _ in range(times):
            win32gui.SendMessage(self._window_hwnd, win32con.WM_VSCROLL, win32con.SB_LINEDOWN, 0)
        return self
    
    def s_scroll_element(self):
        """滚动元素以使其在视图中可见。

        Returns:
            None
        """
        x1,y1,x2,y2 = win32gui.GetWindowRect(self._docu_hwnd)
        y1 = y1 + int((y1*0.4))
        y2 = int(y2 * 0.95)
        while True:
            _current_y = self._current_element.BoundingRectangle.ycenter()
            
            if _current_y  > y1 and _current_y < y2:
                break
            elif _current_y < y1:
                self.s_wheelup()
            elif _current_y > y2:
                self.s_wheeldown()
            time.sleep(0.1)
        return self
        
    # def s_settext(self,text:str) :
    #     self.s_left()
    #     for char in "text":
    #         # 获取字符的 ASCII 码
    #         char_code = ord(char)
    #         # 发送 WM_CHAR 消息
    #         win32gui.SendMessage(self._docu_hwnd, win32con.WM_CHAR, char_code, 0)
    #         # win32gui.PostMessage(self._docu_hwnd, win32con.WM_CHAR, char_code, 0)
    #         time.sleep(0.1)
    #     return self

    def s_settext(self,text:str) :
        self.s_left()
        import win32api
        for char in text:
                    # 获取字符的虚拟键码
                    vk_code = win32api.VkKeyScan(char)
                    # 按下按键
                    win32api.keybd_event(vk_code, 0, 0, 0)
                    # 释放按键
                    win32api.keybd_event(vk_code, 0, win32con.KEYEVENTF_KEYUP, 0)
                    time.sleep(1)
        return self

    def s_hover(self):
        """模拟鼠标悬停在当前元素中心。

        Returns:
            Custom_WebUI: 当前实例。
        """
        # 获取元素的中心坐标
        _center_x = self._current_element.BoundingRectangle.left + self._current_element.BoundingRectangle.width() / 2
        _center_y = self._current_element.BoundingRectangle.top + self._current_element.BoundingRectangle.height() / 2
        
        # 获取窗口的左上角坐标
        left, top, _, _ = win32gui.GetWindowRect(self._window_hwnd)
        
        # 将坐标转换为相对窗口的坐标
        relative_x = int(_center_x - left)
        relative_y = int(_center_y - top)
        
        # 将坐标转换为 Windows 消息所需的格式
        lParam = relative_y << 16 | relative_x
        
        # 发送鼠标移动消息
        win32gui.SendMessage(self._window_hwnd, win32con.WM_MOUSEMOVE, 0, lParam)
        
        # 添加延迟以确保消息被处理
        time.sleep(0.1)
        
        return self

if __name__ == "__main__":
    print("提示 : 模块测试")
    ui = Custom_WebUI()    
    ui.update_doc().find(Name="深度思考")
    
